interface Type {
    getName():string;
    toString():string;
}

class _Symbol {
    name:string;
    type:Type | null;
    scope:Scope | null;

    constructor(name:string,type:Type | null = null){
        this.name = name;
        this.type = type;
        this.scope = null;
    }

    getName(){
        return this.name;
    }

    toString(){
        if(this.type){
            return '<'+this.getName()+':'+this.type.toString()+'>';
        }else{
            return this.getName();
        }
    }
}

class VariableSymbol extends _Symbol {

}

class BuiltInSymbol extends _Symbol implements Type {
    toString(){
        return this.name;
    }
}

interface Scope {
    getScopeName():string;
    getEnclosingScope():Scope | null;
    // add(name,_Symbol)
    define(sym:_Symbol):void;
    // get(name)=>_Symbol
    resolve(name:string):_Symbol | null;
}

/**
 * 基础作用域
 */
class BaseScope implements Scope {
    enclosingScope:Scope | null;
    // int IntSym
    // float FloatSym
    symbols:Map<string,_Symbol> = new Map();
    constructor(enclosingScope:Scope | null){
        this.enclosingScope = enclosingScope;
    }
    getScopeName(): string {
        throw new Error("Method not implemented.");
    }
    getEnclosingScope(): Scope | null {
        return this.enclosingScope;
    }
    define(sym: _Symbol): void {
        this.symbols.set(sym.name,sym);
    }
    resolve(name: string): _Symbol | null {
        let s = this.symbols.get(name);
        if(s){
            return s as _Symbol;
        }
        if(this.enclosingScope){
            return this.enclosingScope.resolve(name);
        }
        return null;
    }
    toString(){
        let buf = '';
        buf += this.getScopeName() + ": {";
        this.symbols.forEach(s => {
            buf += s.name+"=" + s.toString()+","
        })
        buf += '}'
        return buf;
    }
}
// 全局作用域的符号表
class GlobalScope extends BaseScope {
    constructor(){
        super(null);
    }
    getScopeName(): string {
        return 'globals';
    }
   
}

class LocalScope extends BaseScope{
    constructor(parent:Scope){
        super(parent);
    }

    getScopeName(){
        return 'local'
    }
}

class MethodSymbol extends _Symbol implements Scope{
    orderedAgrs:Map<string,_Symbol> = new Map();
    enclosingScope:Scope;

    constructor(name:string,retType:Type,enclosingScope:Scope){
        super(name,retType);
        this.enclosingScope = enclosingScope;
    }

    getScopeName(): string {
       return this.name;
    }
    getEnclosingScope(): Scope | null {
        return this.enclosingScope;
    }
    define(sym: _Symbol): void {
        this.orderedAgrs.set(sym.name,sym);
        sym.scope = this;
    }
    resolve(name: string): _Symbol | null {
        let s = this.orderedAgrs.get(name);
        if(s){
            return s;
        }
        if(this.enclosingScope){
            return (this.getEnclosingScope() as Scope).resolve(name);
        }
        return null
    }
    toString(){
        let buf = 'method<';
        this.orderedAgrs.forEach(s => {
            buf += s.name+"=" + s.toString()+","
        })
        buf +='>:'
        buf += super.toString();
        return buf;
    }
}
class BuiltInTypeSymbol extends _Symbol implements Type {
    constructor(name: string) {
        super(name, null);
    }
}

class SymbolTable {
    globals:GlobalScope = new GlobalScope();

    constructor(){
        this.initTypeSystem();
    }
    initTypeSystem(){
        this.globals.define(new BuiltInTypeSymbol("int"));
        this.globals.define(new BuiltInTypeSymbol("float"));
        this.globals.define(new BuiltInTypeSymbol("void"));
    }
    toString(){
        return this.globals.toString();
    }
}

export {
    SymbolTable,
    Scope,
    GlobalScope,
    VariableSymbol,
    LocalScope,
    MethodSymbol,
    Type,
    _Symbol
}